﻿using DG.Tweening;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

using Object = UnityEngine.Object;

namespace PP.Basic
{
    /// <summary>
    /// AnimationTrigger 动画事件触发器
    /// </summary>
    [DisallowMultipleComponent]
    [RequireComponent(typeof(Animator))]
    public class AnimationTrigger : MonoBehaviour
    {
        private const string Tag = nameof(AnimationTrigger);

        // Public Variables Begin

        // 动画播放过程中的AnimationEvent事件对象
        public event Action<AnimationEventType> AnimationTypeEvent;
        public event Action<string> AnimationNameEvent;
        public event Action<int> AnimationIntEvent;
        public event Action<float> AnimationFloatEvent;
        public event Action<Object> AnimationObjectEvent;

        // 动画标识Id(非必需)
        public string Id
        {
            get => _id;
            set => _id = value;
        }
        // Public Variables End

        // Private Variables Begin

        // 当前动画状态信息
        private AnimatorStateInfo CurrentStateInfo => _animator.GetCurrentAnimatorStateInfo(0);
        // 当前动画片段
        private AnimationClip CurrentClip => _animator.GetCurrentAnimatorClipInfo(0)?[0].clip;
        // 当前动画名称
        private string CurrentClipName => CurrentClip?.name;

        [Tooltip("动画标识Id(非必需)")]
        [SerializeField]
        private string _id;

        [Tooltip("是否启用日志")]
        [SerializeField]
        private bool _isLogEnabled = false;
		
		// Animator 动画控制器
        private Animator _animator;
        private string _currentStateName;
        public bool IsPlaying => _isPlaying;
        private bool _isPlaying;

        // Play方法传入的动画事件回调方法，动画播放完毕后清空
        private Action<float> _animationFloatHandler;
        private Action<int> _animationIntHandler;
        private Action<string> _animationNameHandler;
        private Action<AnimationEventType> _animationTypeHandler;
        private Action<Object> _animationObjectHandler;

        private bool _isInited = false;
        // Private Variables End

        // Public Methods Begin
        /// <summary>
        /// 播放动画
        /// </summary>
        /// <param name="stateName">Animator的StateMachine状态机中的StateName状态名，建议每个StateName状态名与AnimationClip的文件名保持一致。</param>
        /// <param name="onComplete">动画播放完成后的回调方法</param>
        /// <param name="normalizedStartTime">开始播放的时间点，[0, 1]区间内容的标准花时间</param>
        /// <param name="layer">Animator中的Layer。-1则表示从所有层中尝试找到StateName的状态进行播放。</param>
        /// <param name="isLoop">是否循环播放动画。要进行循环播放，还需要在资源中启用AnimationClip的Loop Pose选项。</param>
        public void Play(string stateName, Action onComplete = null, float normalizedStartTime = 0f, int layer = -1, bool isLoop = false)
        {
            if (string.IsNullOrEmpty(stateName))
            {
                Debug.LogError($"{Tag}.Play# stateName is null or empty.");
                return;
            }

            // 如果当前GameObject不是激活状态，则不播放动画，因为这个状态_animator无法正常工作。
            if (!gameObject.activeInHierarchy)
            {
                return;
            }

            Initiate();

            if (!_animator.HasState(stateName, layer))
            {
                return;
            }

            _currentStateName = stateName;

            normalizedStartTime = Mathf.Clamp01(normalizedStartTime);

            // 停止当前动画
            StopAnimation();

            // 重新播放动画
            _isPlaying = true;
            _animator.enabled = true;
            _animator.Play(stateName, layer, normalizedStartTime);
            _animator.Update(0);

            var duration = _animator.GetClipDuration(stateName);
            // 剩余时间
            var leftDuration = duration * (1.0f - normalizedStartTime);
            leftDuration = Mathf.Max(0f, leftDuration);

            Log($"{Tag}.Play# In. stateName: {stateName}, normalizedStartTime: {normalizedStartTime}, layer: {layer}, isLoop: {isLoop}");

            DelayedCall(leftDuration, () =>
            {
                _isPlaying = false;
                Log($"{Tag}.Play# onComplete# stateName: {stateName}");
                if (!isLoop)
                {
                    StopAnimation();
                }
                else
                {
                    Play(stateName, onComplete, 0, layer, isLoop);
                }
                onComplete?.Invoke();
            });
        }

        /// <summary>
        /// Layer -1 is the same as layer 0, but GetCurrentAnimatorStateInfo(-1) is invalid so use 0 as default instead.
        /// </summary>
        /// <param name="layer"></param>
        /// <returns></returns>
        public float GetAnimNormalizedTime(int layer = 0)
        {
            AnimatorStateInfo stateInfo = _animator.GetCurrentAnimatorStateInfo(layer);
            float normalizedTime = stateInfo.normalizedTime;
            return normalizedTime;
        }

        /// <summary>
        /// 播放动画，带有Float value类型的动画事件回调
        /// </summary>
        public void PlayEventFloat(string stateName, Action<float/*value*/> onAnimationEvent = null, float normalizedStartTime = 0f, int layer = -1, bool isLoop = false)
        {
            Play(stateName, null, normalizedStartTime, layer, isLoop);
            _animationFloatHandler = onAnimationEvent;
        }
        
        /// <summary>
        /// 播放动画，带有Int value类型的动画事件回调
        /// </summary>
        public void PlayEventInt(string stateName, Action<int/*value*/> onAnimationEvent = null, float normalizedStartTime = 0f, int layer = -1, bool isLoop = false)
        {
            Play(stateName, null, normalizedStartTime, layer, isLoop);
            _animationIntHandler = onAnimationEvent;
        }

        /// <summary>
        /// 播放动画，带有String eventName类型的动画事件回调
        /// </summary>
        public void PlayEventName(string stateName, Action<string/*eventName*/> onAnimationEvent = null, float normalizedStartTime = 0f, int layer = -1, bool isLoop = false)
        {
            Play(stateName, null, normalizedStartTime, layer, isLoop);
            _animationNameHandler = onAnimationEvent;
        }

        /// <summary>
        /// 播放动画，带有AnimationEventType eventType类型的动画事件回调
        /// </summary>
        public void PlayEventType(string stateName, Action<AnimationEventType/*value*/> onAnimationEvent = null, float normalizedStartTime = 0f, int layer = -1, bool isLoop = false)
        {
            Play(stateName, null, normalizedStartTime, layer, isLoop);
            _animationTypeHandler = onAnimationEvent;
        }

        /// <summary>
        /// 播放动画，带有UnityEngine.Object obj类型的动画事件回调
        /// </summary>
        public void PlayEventObject(string stateName, Action<Object/*value*/> onAnimationEvent = null, float normalizedStartTime = 0f, int layer = -1, bool isLoop = false)
        {
            Play(stateName, null, normalizedStartTime, layer, isLoop);
            _animationObjectHandler = onAnimationEvent;
        }

        /// <summary>
        /// 停止动画
        /// </summary>
        public void StopAnimation()
        {
            _isPlaying = false;
            if (_animator)
                _animator.enabled = false;
            StopAllCoroutines();
            _coDelayedCall = null;

            _animationFloatHandler = null;
            _animationIntHandler = null;
            _animationNameHandler = null;
            _animationTypeHandler = null;
            _animationObjectHandler = null;
        }

        /// <summary>
        /// 暂停动画到指定时间点
        /// </summary>
        /// <param name="stateName">动画状态名，如果空则使用当前正在播放的动画状态</param>
        /// <param name="normalizedTime">指定的时间点，[0, 1]标准化的时间</param>
        /// <param name="layer"></param>
        public void PauseAnimation(string stateName = null, float normalizedTime = 0f, int layer = -1)
        {
            Initiate();

            if (string.IsNullOrEmpty(stateName))
                stateName = _currentStateName;
            else
                _currentStateName = stateName;

            // 停止当前动画
            StopAnimation();

            // 回退一帧，再由下面的Update，刷新一帧，使之停止在正确时间
            normalizedTime -= 0.001f;
            normalizedTime = Mathf.Clamp01(normalizedTime);

            // 启用动画播放器
            _animator.enabled = true;
            // 播放动画状态
            _animator.Play(stateName, layer, normalizedTime);
            // 刷新动画
            _animator.Update(0);
            // 停止动画
            _animator.enabled = false;
        }

        public float GetClipDuration(string clipName)
        {
            return _animator?.GetClipDuration(clipName) ?? 0f;
        }

        // Public Methods End

        // Lifecycle Methods Begin
        private void Awake()
        {
            Initiate();
        }

        public virtual void Initiate()
        {
            if (_isInited)
                return;
            _isInited = true;
            OnInit();
        }

        private void OnInit()
        {
            _animator = GetComponent<Animator>();
        }

        private void OnDisable()
        {
            StopAnimation();
        }

        private void OnDestroy()
        {
            StopAnimation();

            AnimationFloatEvent = null;
            AnimationIntEvent = null;
            AnimationNameEvent = null;
            AnimationTypeEvent = null;
            AnimationObjectEvent = null;
        }
        // Lifecycle Methods End

        // Event Methods Begin
        public void OnAnimationEventFloat(float value)
        {
            Log($"{Tag}.OnAnimationEventFloat# clipName: {CurrentClipName}, value: {value}");
            AnimationFloatEvent?.Invoke(value);
            _animationFloatHandler?.Invoke(value);
        }

        public void OnAnimationEventInt(int value)
        {
            Log($"{Tag}.OnAnimationEventInt# clipName: {CurrentClipName}, value: {value}");
            AnimationIntEvent?.Invoke(value);
            _animationIntHandler?.Invoke(value);
        }

        public void OnAnimationEventName(string eventName)
        {
            Log($"{Tag}.OnAnimationEventName# clipName: {CurrentClipName}, eventName: {eventName}");
            AnimationNameEvent?.Invoke(eventName);
            _animationNameHandler?.Invoke(eventName);
        }

        public void OnAnimationEventType(AnimationEventType eventType)
        {
            Log($"{Tag}.OnAnimationEventType# clipName: {CurrentClipName}, eventType: {eventType}");
            AnimationTypeEvent?.Invoke(eventType);
            _animationTypeHandler?.Invoke(eventType);
        }

        public void OnAnimationEventObject(Object obj)
        {
            Log($"{Tag}.OnAnimationObjectName# clipName: {CurrentClipName}, obj: {obj}");
            AnimationObjectEvent?.Invoke(obj);
            _animationObjectHandler?.Invoke(obj);
        }

        /// <summary>
        /// 播放动画的音频事件
        /// </summary>
        /// <param name="audioClipObj">音频文件对象</param>
        public void OnAnimationEventAudio(Object audioClipObj)
        {
            Log($"{Tag}.OnAnimationEventAudio# clipName: {CurrentClipName}, audioClipObj: {audioClipObj?.name}");
            var audioClip = audioClipObj as AudioClip;
            if (audioClip)
            {
                //this.PlayAudio(audioClip);
            }
        }
        // Event Methods End

        // Private Methods Begin
        private Coroutine _coDelayedCall;
        private void DelayedCall(float delay, Action onComplete)
        {
            if (_coDelayedCall != null)
                StopCoroutine(_coDelayedCall);

            _coDelayedCall = StartCoroutine(CoDelayedCall(delay, onComplete));
        }

        private IEnumerator CoDelayedCall(float delay, Action onComplete)
        {
            yield return new WaitForSeconds(delay);
            yield return new WaitForEndOfFrame();
            onComplete?.Invoke();
        }

        private void Log(object msg)
        {
            if (_isLogEnabled)
            {
                Debug.Log(msg);
            }
        }
        // Private Methods End
    }
}
